package com.gitee.absolute_spirit.hiot_android.mqtt

import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import com.gitee.absolute_spirit.hiot_android.ACTION_MQTT_ACTION
import org.eclipse.paho.mqttv5.client.IMqttToken
import org.eclipse.paho.mqttv5.client.MqttActionListener
import org.eclipse.paho.mqttv5.client.MqttAsyncClient
import org.eclipse.paho.mqttv5.client.MqttCallback
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions
import org.eclipse.paho.mqttv5.client.MqttDisconnectResponse
import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence
import org.eclipse.paho.mqttv5.common.MqttException
import org.eclipse.paho.mqttv5.common.MqttMessage
import org.eclipse.paho.mqttv5.common.packet.MqttProperties

class MqttService : Service() {

    inner class MqttBinder : Binder() {
        fun getService(): MqttService = this@MqttService
    }

    private val binder = MqttBinder()
    override fun onBind(intent: Intent?): IBinder? {
        return binder
    }


    val tag = "mqtt-service";
    private val serverURI = "tcp://broker.emqx.io:1883"
    private val username = "emqx"
    private val password = "public"
    private val clientId = "kotlin_client"

    private lateinit var mqttAsyncClient: MqttAsyncClient
    private lateinit var options: MqttConnectionOptions

    private var flagConnected: Boolean = false

    fun publish(topic: String, msg: String, qos: Int = 1, retained: Boolean = false) {
        try {
            val message = MqttMessage()
            message.payload = msg.toByteArray()
            message.qos = qos
            message.isRetained = retained
            mqttAsyncClient.publish(topic, message, null, object : MqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken?) {
                    Log.d(tag, "$msg published to $topic")
                }

                override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                    Log.d(tag, "Failed to publish $msg to $topic")
                }
            })
        } catch (e: MqttException) {
            e.printStackTrace()
        }
    }

    fun unsubscribe(topic: String) {
        try {
            mqttAsyncClient.unsubscribe(topic, null, object : MqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken?) {
                    Log.d(tag, "Unsubscribed to $topic")
                }

                override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                    Log.d(tag, "Failed to unsubscribe $topic")
                }
            })
        } catch (e: MqttException) {
            e.printStackTrace()
        }
    }

    fun subscribe(topic: String, qos: Int = 0) {
        if (flagConnected) {
            mqttAsyncClient.subscribe(topic, qos, null, object : MqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken?) {
                    Log.d(tag, "订阅成功：$topic")
                }

                override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                    Log.e(tag, "订阅失败：${exception.toString()}")
                }

            })
        }
    }

    private fun connect() {
        if (flagConnected) return

        mqttAsyncClient.connect(options, null, object : MqttActionListener {
            override fun onSuccess(asyncActionToken: IMqttToken?) {
                flagConnected = true;
            }

            override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                Log.e(tag, exception.toString())
            }
        })
    }

    private fun disconnect() {
        try {
            mqttAsyncClient.disconnect(null, object : MqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken?) {
                    Log.d(tag, "Disconnected")
                }

                override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                    Log.d(tag, "Failed to disconnect")
                }
            })
        } catch (e: MqttException) {
            e.printStackTrace()
        }
    }

    override fun onCreate() {
        super.onCreate()

        mqttAsyncClient = MqttAsyncClient(serverURI, clientId, MemoryPersistence())
        mqttAsyncClient.setCallback(object : MqttCallback {
            override fun disconnected(disconnectResponse: MqttDisconnectResponse?) {
                flagConnected = false
            }

            override fun mqttErrorOccurred(exception: MqttException?) {
                Log.e(tag, exception.toString())
            }

            override fun messageArrived(topic: String?, message: MqttMessage?) {
                if (topic == null) return
                val intent = Intent(ACTION_MQTT_ACTION)
                val bundle = Bundle()
                bundle.putString(topic, message?.payload.toString())
                intent.putExtras(bundle)
                sendBroadcast(intent)
            }

            override fun deliveryComplete(token: IMqttToken?) {
                Log.d(tag, token?.requestMessage.toString())
            }

            override fun connectComplete(reconnect: Boolean, serverURI: String?) {
                Log.i(tag, serverURI.toString())
            }

            override fun authPacketArrived(reasonCode: Int, properties: MqttProperties?) {
                Log.i(tag, properties.toString())
            }

        })

        options = MqttConnectionOptions()
        options.connectionTimeout = 10
        options.keepAliveInterval = 60
        options.userName = username
        options.password = password.toByteArray()
        options.setAutomaticReconnectDelay(10, 60)

        if (!flagConnected) connect()
    }
}